//!
//! # Functiosn of native transactions
//!
//! - Staking
//! - System management
//!

use crate::ledger::{
    staking::{Amount, TmPubKeyBytes},
    StateBranch,
};
use ethereum_types::{H160, U256};
use ruc::*;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Tx {
    pub caller: H160,
    ops_kind: OpsKind,
}

impl Tx {
    // Return used-fee for distributing rewards.
    // NOTE: fee has been charged from the caller if success!
    pub fn apply(self, sb: &mut StateBranch) -> Result<U256> {
        let caller = self.caller;
        let s = &sb.state.staking;
        let token = &sb.state.evm.token;
        match self.ops_kind {
            OpsKind::Stake(info) => s
                .stake_to(
                    token,
                    caller,
                    &info.target.validator,
                    info.target.amount,
                    info.nonce,
                )
                .c(d!()),
            OpsKind::Unstake(info) => {
                if let Some(t) = info.target {
                    s.unstake_from(token, caller, &t.validator, t.amount, info.nonce)
                        .c(d!())
                } else {
                    s.unstake_all(token, caller, info.nonce).c(d!())
                }
            }
        }
    }
}

#[non_exhaustive]
#[derive(Clone, Debug, Deserialize, Serialize)]
enum OpsKind {
    Stake(OpsStake),
    Unstake(OpsUnStake),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
struct OpsStake {
    target: OpsTarget,
    nonce: U256,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
struct OpsUnStake {
    target: Option<OpsTarget>,
    nonce: U256,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
struct OpsTarget {
    validator: TmPubKeyBytes,
    amount: Amount,
}
